-- 这里是littledro对排队论做的注释 可以从151行开始看，方便理解排队论是怎么工作的
local _G = GLOBAL
if _G.TheNet:IsDedicated() or _G.TheNet:GetServerGameMode() == "lavaarena" then return end
TUNING.ACTION_QUEUE_DEBUG_MODE = GetModConfigData("debug_mode") -- 注册debug_mode到全局变量,用来在actionqueue中输出debug信息
-- 用到的全局变量
local SpawnPrefab = _G.SpawnPrefab
local TheInput = _G.TheInput
local unpack = _G.unpack
local CONTROL_ACTION = _G.CONTROL_ACTION
local CONTROL_FORCE_INSPECT = _G.CONTROL_FORCE_INSPECT
local CONTROL_FORCE_TRADE = _G.CONTROL_FORCE_TRADE
local PLAYERCOLOURS = _G.PLAYERCOLOURS
local STRINGS = _G.STRINGS
local ActionQueuer
local ThePlayer
local TheWorld

PLAYERCOLOURS.WHITE = {1, 1, 1, 1}

Assets = {
    Asset("ATLAS", "images/selection_square.xml"),
    Asset("IMAGE", "images/selection_square.tex"),
}

local interrupt_controls = {}   -- 这个数组用来控制什么操作可以打断排队论
for control = _G.CONTROL_ATTACK, _G.CONTROL_MOVE_RIGHT do   -- 这是一个数值循环,在scripts/constants下可以看到对应的数值
    interrupt_controls[control] = true  -- 2-12数值代表的控制操作将会打断排队论工作队列
end

-- 220225 null: support for littledro's QAAQ mod
local qaaq = GetModConfigData("qaaq")   -- 这是给qaaq做的兼容
if qaaq then
	interrupt_controls[_G.CONTROL_ACTION] = false   -- qaaq的提灯砍树需要让空格不能打断排队论工作队列
end
-- 这是鼠标控制键,为了避免玩家改键,鼠标左键(主操作)为false,鼠标右键(次操作)为true,鼠标操作贯穿整个排队论对实体的选择，这里非常重要
local mouse_controls = {[_G.CONTROL_PRIMARY] = false, [_G.CONTROL_SECONDARY] = true}    

local function GetKeyFromConfig(config) -- 基础的从modinfo拿配置项的按键
    local key = GetModConfigData(config, true)
    if type(key) == "string" and _G:rawget(key) then
        key = _G[key]
    end
    return type(key) == "number" and key or -1  
end

local function InGame() -- 检测是否在游戏中
    return ThePlayer and ThePlayer.HUD and not ThePlayer.HUD:HasInputFocus()
end

local turf_grid = {}
local turf_size = 4
local turf_grid_visible = false
local turf_grid_radius = GetModConfigData("turf_grid_radius")
local turf_grid_color = PLAYERCOLOURS[GetModConfigData("turf_grid_color")]
TheInput:AddKeyUpHandler(GetKeyFromConfig("turf_grid_key"), function()  -- 显示地皮网格
    if not InGame() then return end
    if turf_grid_visible then
        for _, grid in pairs(turf_grid) do
            grid:Hide()
        end
        turf_grid_visible = false
        return
    end
    local center_x, _, center_z = TheWorld.Map:GetTileCenterPoint(ThePlayer.Transform:GetWorldPosition())
    local radius = turf_grid_radius * turf_size
    local count = 1
    for x = center_x - radius, center_x + radius, turf_size do
        for z = center_z - radius, center_z + radius, turf_size do
            if not turf_grid[count] then
                turf_grid[count] = SpawnPrefab("gridplacer")
                turf_grid[count].AnimState:SetAddColour(unpack(turf_grid_color))
            end
            turf_grid[count].Transform:SetPosition(x, 0, z)
            turf_grid[count]:Show()
            count = count + 1
        end
    end
    turf_grid_visible = true
end)

-- 220225 null: support for littledro's QAAQ mod
local collect_mod = {turn_on = "normal", turn_off = false, chop_mod = "chop_mod"} -- 自动收集模式增加选项，在这三者中间切换

TheInput:AddKeyUpHandler(GetKeyFromConfig("auto_collect_key"), function()   -- 自动收集模式按键开关
    if not InGame() then return end

    if qaaq then -- 220225 null: support for littledro's QAAQ mod
        ActionQueuer.auto_collect = collect_mod.turn_on
        collect_mod.turn_on = collect_mod.chop_mod
        collect_mod.chop_mod = collect_mod.turn_off
        collect_mod.turn_off = ActionQueuer.auto_collect
    else
        ActionQueuer.auto_collect = not ActionQueuer.auto_collect -- 220225 null: original autocollect toggle
    end
    ThePlayer.components.talker:Say("Auto Collect: "..tostring(ActionQueuer.auto_collect))
end)

TheInput:AddKeyUpHandler(GetKeyFromConfig("endless_deploy_key"), function() -- 无尽部署模式按键
    if not InGame() then return end
    ActionQueuer.endless_deploy = not ActionQueuer.endless_deploy
    ThePlayer.components.talker:Say("Endless deploy: "..tostring(ActionQueuer.endless_deploy))
end)

local last_recipe, last_skin
TheInput:AddKeyUpHandler(GetKeyFromConfig("last_recipe_key"), function()    -- 制作上一个物品的按键
    if not InGame() then return end
    if not last_recipe then
        ThePlayer.components.talker:Say("No previous recipe found")
        return
    end
    local last_recipe_name = STRINGS.NAMES[last_recipe.name:upper()] or "UNKNOWN"
    local builder = ThePlayer.replica.builder
    if not builder:CanBuild(last_recipe.name) and not builder:IsBuildBuffered(last_recipe.name) then
        ThePlayer.components.talker:Say("Unable to craft: "..last_recipe_name)  -- 无法制作上一个物品
        return
    end
    if last_recipe.placer then  -- 放置建筑类
        if not builder:IsBuildBuffered(last_recipe.name) then
            builder:BufferBuild(last_recipe.name)
        end
        ThePlayer.components.playercontroller:StartBuildPlacementMode(last_recipe, last_skin)
    else
        builder:MakeRecipeFromMenu(last_recipe, last_skin)  -- 制作上一个物品
    end
    ThePlayer.components.talker:Say("Crafting last recipe: "..last_recipe_name)
end)

local function ActionQueuerInit()   -- 排队论初始化配置
    print("[ActionQueue] Adding ActionQueuer component")
    ThePlayer:AddComponent("actionqueuer")
    ActionQueuer = ThePlayer.components.actionqueuer
    ActionQueuer.double_click_speed = GetModConfigData("double_click_speed")
    ActionQueuer.double_click_range = GetModConfigData("double_click_range")
    ActionQueuer.deploy_on_grid = GetModConfigData("deploy_on_grid")
    ActionQueuer.auto_collect = GetModConfigData("auto_collect")
    ActionQueuer.endless_deploy = GetModConfigData("endless_deploy")
    ActionQueuer:SetToothTrapSpacing(GetModConfigData("tooth_trap_spacing"))
    ActionQueuer:SetFarmGrid(GetModConfigData("farm_grid")) -- 201221 null: added support for changing farm grids (3x3, 4x4)
    ActionQueuer:SetDoubleSnake(GetModConfigData("double_snake")) -- 210127 null: added support for snaking within snaking
    ActionQueuer:SetAttackQueue(GetModConfigData("attack_queue")) -- 210307 null: enable or disable ATTACK queuing
    local r, g, b = unpack(PLAYERCOLOURS[GetModConfigData("selection_color")])
    ActionQueuer:SetSelectionColor(r, g, b, GetModConfigData("selection_opacity"))
end

local action_queue_key = GetKeyFromConfig("action_queue_key")   -- 从modinfo获取设置的排队论按键
--maybe i won't need this one day...
local use_control = TheInput:GetLocalizedControl(0, CONTROL_FORCE_TRADE) == STRINGS.UI.CONTROLSSCREEN.INPUTS[1][action_queue_key]
action_queue_key = use_control and CONTROL_FORCE_TRADE or action_queue_key
TheInput.IsAqModifierDown = use_control and TheInput.IsControlPressed or TheInput.IsKeyDown
local always_clear_queue = GetModConfigData("always_clear_queue")
AddComponentPostInit("playercontroller", function(self, inst)   -- 这里是排队论工作队列的主逻辑，通过对playercontroller组件的修改实现的
    if inst ~= _G.ThePlayer then return end
    ThePlayer = _G.ThePlayer
    TheWorld = _G.TheWorld
    ActionQueuerInit()

    local PlayerControllerOnControl = self.OnControl    -- 保存系统的OnControl函数
    self.OnControl = function(self, control, down)
        local mouse_control = mouse_controls[control]   -- control参数与line36的数组匹配,左键false，右键true
        if mouse_control ~= nil then    -- 只有鼠标左右键才会进入流程，其他操作都是nil，不会进入
            if down then    -- （鼠标）按下时进行（选择操作）
                if TheInput:IsAqModifierDown(action_queue_key) then -- 如果排队论按键被按下 则执行下面操作
                    local target = TheInput:GetWorldEntityUnderMouse()  -- 这里是对钓鱼模式进行适配，target为鼠标下的实体
                    if target and target:HasTag("fishable") and not inst.replica.rider:IsRiding()   -- 如果目标为真，而且拥有可钓鱼e标签，而且玩家不在骑行
                      and inst.replica.inventory:EquipHasTag("fishingrod") then -- 而且玩家装备着鱼竿
                        ActionQueuer:StartAutoFisher(target)    -- 就进行钓鱼队列
                    elseif not ActionQueuer.auto_fishing then   -- 如果钓鱼队列是关闭的
                        ActionQueuer:OnDown(mouse_control)  -- 那么进行OnDown()操作（选择操作）
                    end
                    return
                end
            else    
                ActionQueuer:OnUp(mouse_control)    -- 鼠标抬起时进行OnUp(应用操作)
            end
        end
        PlayerControllerOnControl(self, control, down)  -- 这里返回原版的OnControl函数
        if down and ActionQueuer.action_thread and not ActionQueuer.selection_thread and InGame()   -- 如果有键按下，动作线程为真，选择线程不为真...
          and (interrupt_controls[control] or mouse_control ~= nil and not TheInput:GetHUDEntityUnderMouse()) then  -- interrupt_controls[]会打断正在进行的动作线程
            ActionQueuer:ClearActionThread()
            if always_clear_queue or control == CONTROL_ACTION then -- 如果打开了always_clear_queue选项或者使用空格，则会清除选择线程
                ActionQueuer:ClearSelectedEntities()
            end
        end
    end
    local PlayerControllerIsControlPressed = self.IsControlPressed
    self.IsControlPressed = function(self, control)
        if control == CONTROL_FORCE_INSPECT and ActionQueuer.action_thread then return false end    -- 输入为CONTROL_FORCE_INSPECT而且动作线程为真，就返回false
        
        -- 201220 null: fix for EAT on self
        if use_control and control == CONTROL_FORCE_TRADE and   -- 输入为CONTROL_FORCE_TRADE键，而且鼠标上有东西返回false
           ThePlayer.components.playeravatardata.inst.replica.inventory:GetActiveItem() ~= nil then return false end
        
        return PlayerControllerIsControlPressed(self, control)  -- 不是上面两种则正常返回
    end
end)

AddClassPostConstruct("components/builder_replica", function(self)  -- 修改系统重复制作组件
    local BuilderReplicaMakeRecipeFromMenu = self.MakeRecipeFromMenu
    self.MakeRecipeFromMenu = function(self, recipe, skin)
        last_recipe, last_skin = recipe, skin   -- 保存上一次制作的物体和用的皮肤
        if not ActionQueuer.action_thread and TheInput:IsAqModifierDown(action_queue_key)   -- 按下shift进入重复制作队列
          and not recipe.placer and self:CanBuild(recipe.name) then
            ActionQueuer:RepeatRecipe(self, recipe, skin)
        else
            BuilderReplicaMakeRecipeFromMenu(self, recipe, skin)    -- 否则进行原版制作
        end
    end
    local BuilderReplicaMakeRecipeAtPoint = self.MakeRecipeAtPoint
    self.MakeRecipeAtPoint = function(self, recipe, pt, rot, skin)
        last_recipe, last_skin = recipe, skin   -- 还是保存皮肤，这个是建筑类的放点上
        BuilderReplicaMakeRecipeAtPoint(self, recipe, pt, rot, skin)
    end
end)

AddComponentPostInit("highlight", function(self, inst)  -- 用系统高亮组件给排队论选择的实体进行高亮
    local HighlightHighlight = self.Highlight
    self.Highlight = function(self, ...)    -- 高亮
        if ActionQueuer.selection_thread or ActionQueuer:IsSelectedEntity(inst) then return end
        HighlightHighlight(self, ...)
    end
    local HighlightUnHighlight = self.UnHighlight
    self.UnHighlight = function(self)   -- 去高亮
        if ActionQueuer:IsSelectedEntity(inst) then return end
        HighlightUnHighlight(self)
    end
end)
--for minimizing the memory leak in geo
--hides the geo grid during an action queue
AddComponentPostInit("placer", function(self, inst)
    local PlacerOnUpdate = self.OnUpdate
    self.OnUpdate = function(self, ...)
        self.disabled = ActionQueuer.action_thread ~= nil   -- 排队论工作队列中不显示网格
        PlacerOnUpdate(self, ...)
    end
end)
